home *** CD-ROM | disk | FTP | other *** search
/ MacWorld 1998 March / Macworld (1998-03) (Disk 1).dmg / Shareware World / Utilities / Text Processing / Alpha / Tcl / Modes / diffMode.tcl < prev    next >
Encoding:
Text File  |  1997-12-10  |  27.3 KB  |  1,005 lines  |  [TEXT/ALFA]

  1. ## -*-Tcl-*-
  2.  # ###################################################################
  3.  #  Vince's Additions - an extension package for Alpha
  4.  # 
  5.  #  FILE: "diffMode.tcl"
  6.  #                                    created: 7/3/95 {11:15:02 pm} 
  7.  #                                last update: 10/12/97 {1:57:55 pm} 
  8.  #  Author: Vince Darley
  9.  #  E-mail: <darley@fas.harvard.edu>
  10.  #    mail: Division of Engineering and Applied Sciences, Harvard University
  11.  #          Oxford Street, Cambridge MA 02138, USA
  12.  #     www: <http://www.fas.harvard.edu/~darley/>
  13.  #  
  14.  # improvements Copyright (c) 1997  Vince Darley
  15.  # 
  16.  #  Description: 
  17.  #  
  18.  #  Largely re-written Diff mode for Alpha.  Still under construction,
  19.  #  but already a lot better than the old one.  Basic features:
  20.  #  
  21.  #  A 'Diff' menu, which contains commonly used options.
  22.  #  
  23.  #  Uses Alpha's 'marks' so that you can patch diffs back and forth
  24.  #  between files without losing the correct location in the file.
  25.  #  (previously if you modified one of the original windows, all line
  26.  #  numbers after that would be incorrect)
  27.  #  
  28.  #  Limitations:
  29.  #  
  30.  #  Sadly a lot of Alpha's window manipulation commands only work
  31.  #  on the foremost window.  This means this code is slowed down a
  32.  #  lot because it often has to bring a window to the front before
  33.  #  reading/writing into it.  There is a flag to setup a hack which
  34.  #  helps with this, at the expense of colours in the windows.
  35.  # 
  36.  #  History:
  37.  # 
  38.  #  modified by  rev reason
  39.  #  -------- --- --- -----------
  40.  #  7/3/95   Pete? 1.0 original
  41.  #  3/9/97   VMD 2.0 much improved version
  42.  # ###################################################################
  43.  ##
  44.  
  45. # Usage: diff [-#] [-abBcdefhHilnNprstTuvw] [-C lines] [-F regexp] [-I regexp]
  46. #        [-L label [-L label]] [-S file] [-D symbol] [+ignore-blank-lines]
  47. #        [+context[=lines]] [+unified[=lines]] [+ifdef=symbol]
  48. #        [+show-function-line=regexp]
  49. #        [+speed-large-files] [+ignore-matching-lines=regexp] [+new-file]
  50. #        [+initial-tab] [+starting-file=file] [+text] [+all-text] [+ascii]
  51. #        [+minimal] [+ignore-space-change] [+ed] [+reversed-ed] [+ignore-case]
  52. #        [+print] [+rcs] [+show-c-function] [+binary] [+brief] [+recursive]
  53. #        [+report-identical-files] [+expand-tabs] [+ignore-all-space]
  54. #        [+file-label=label [+file-label=label]] [+version] path1 path2
  55.  
  56. alpha::mode Diff 2.5.2 diffMenu {*.diff *.patch} {diffMenu} {
  57.     alpha::package require Alpha 7.0fc3
  58.     addMenu diffMenu •288
  59.     namespace eval compare {}
  60.     menu::insert Utils submenu 0 compare
  61.     menu::insert compare items end "windows" "files…" "directories…"
  62.     hook::register requireOpenWindowsHook [list compare windows] 2
  63. } uninstall {
  64.     removeFile "$pkg_file"
  65.     removeFile "${HOME}:Tools:GNU Diff"
  66. } maintainer {
  67.     "Vince Darley" darley@fas.harvard.edu <http://www.fas.harvard.edu/~darley/>
  68. } help {file "Diff Help"}
  69.  
  70. proc diffMenu {} {}
  71.  
  72. newPref f useSophisticatedDiffMarking 1 Diff
  73. newPref f useFastWindowSwapping 1 Diff
  74. newPref f useMarksDontBringToFront 1 Diff
  75. newPref f synchroniseMoveAndView 1 Diff Diff::bindUpDown
  76. newPref f workaroundAlphaColourBug 1 Diff
  77. newPref var linesOfContext 3 Diff
  78. newPref var diffFlags { } Diff
  79. newPref f convertSlashToColonInPaths 1 Diff
  80. newPref v removeFilePrefix "" Diff
  81.  
  82. menu -n $diffMenu -p Diff::menuProc -M Diff {
  83.     "rerunDiff"
  84.     "(-"
  85.     "/<I<BpatchIntoLeftWindow"
  86.     "/<I<BpatchIntoRightWindow"
  87.     "(-"
  88.     "cleanUpAndCloseWindows"
  89.     "(-"
  90.     "locateLeftWindow"
  91.     "locateRightWindow"
  92.     "locateLeftDir"
  93.     "locateRightDir"
  94.     "parseDiffWin"
  95. }
  96. # bind manually due to bug
  97. bind 0x7b <z> Diff::patchIntoLeftWindow Diff
  98. bind 0x7c <z> Diff::patchIntoRightWindow Diff
  99. bind 0x7b <oz> Diff::patchIntoLeftWindow Diff
  100. bind 0x7c <oz> Diff::patchIntoRightWindow Diff
  101. # do the rest
  102. bind '\r'        Diff::Select    Diff
  103. bind '\t'        Diff::View    Diff
  104. bind Kpad. <c>        Diff::Win
  105. bind Enter        {Diff::Down;Diff::Select}    Diff
  106. bind Kpad0        {Diff::Up;Diff::Select}    Diff
  107.  
  108. hook::register closeHook Diff::closing Diff
  109. hook::register openHook Diff::opening Diff
  110.  
  111. proc Diff::bindUpDown {} {
  112.     global DiffmodeVars
  113.     if $DiffmodeVars(synchroniseMoveAndView) {
  114.         catch {unbind down         Diff::Down Diff}
  115.         catch {unbind up         Diff::Up Diff}
  116.         bind down        {Diff::Down;Diff::View}    Diff
  117.         bind up        {Diff::Up;Diff::View}    Diff
  118.     } else {
  119.         catch {unbind down        {Diff::Down;Diff::View}    Diff}
  120.         catch {unbind up        {Diff::Up;Diff::View}    Diff}
  121.         bind down         Diff::Down Diff
  122.         bind up         Diff::Up Diff
  123.     }
  124. }
  125.  
  126. Diff::bindUpDown
  127.  
  128. proc Diff::menuProc {menu item} {
  129.     Diff::$item
  130. }
  131.  
  132. proc Diff::locateLeftWindow {} {
  133.     global Diff::1
  134.     set Diff::1 [getfile "Select your left (old) file:"]
  135.     Diff::Display Diff::1 1 0 1
  136.     Diff::setMarksUp
  137.     if [info exists Diff::1] {Diff::mark ${Diff::1} 1 ""}
  138.     Diff::diffWinFront
  139. }
  140.  
  141. proc Diff::locateRightWindow {} {
  142.     global Diff::2
  143.     set Diff::2 [getfile "Select your right (new) file:"]
  144.     Diff::Display Diff::2 0 0 1
  145.     Diff::setMarksUp
  146.     if [info exists Diff::2] {Diff::mark ${Diff::2} 0 ""}
  147.     Diff::diffWinFront
  148. }
  149.  
  150. proc Diff::locateLeftDir {} {
  151.     global Diff::leftDir
  152.     set Diff::leftDir [get_directory "Select your left (old) directory:"]
  153.     append Diff::leftDir :
  154. }
  155. proc Diff::locateRightDir {} {
  156.     global Diff::rightDir
  157.     set Diff::rightDir [get_directory "Select your right (new) directory:"]
  158.     append Diff::rightDir :
  159. }
  160.  
  161. proc Diff::rerunDiff {} {
  162.     global diffDir
  163.     Diff::diffWinFront
  164.     killWindow
  165.     if {$diffDir} {
  166.         doTheCompare 1 {* Directory Comparison *}
  167.     } else {
  168.         Diff::files
  169.     }
  170. }
  171.  
  172. proc Diff::cleanUpAndCloseWindows {} {
  173.     global Diff::1 Diff::2 diffDir
  174.     if ![catch {bringToFront ${Diff::1}}] {
  175.         removeAllMarks diff-*
  176.         shrinkFull
  177.         killWindow
  178.     }
  179.     
  180.     if ![catch {bringToFront ${Diff::2}}] {
  181.         removeAllMarks diff-*
  182.         shrinkFull
  183.         killWindow
  184.     }
  185.     Diff::diffWinFront
  186.     killWindow
  187. }
  188.  
  189. proc Diff::closing {{name ""}} {
  190.     global Diff::array Diff::Marked Diff::1 Diff::2
  191.     foreach var [info globals Diff::array*] {
  192.         global $var
  193.         if [array exists $var] { unset $var }
  194.     }
  195.     catch {unset Diff::Marked}
  196.     catch {unset Diff::1}
  197.     catch {unset Diff::2}
  198. }
  199.  
  200. ## 
  201.  # -------------------------------------------------------------------------
  202.  # 
  203.  # "Diff::opening" --
  204.  # 
  205.  #  This procedure is called whenever we open a diff window, whether 
  206.  #  a '.diff' file, or whether a window produced by this mode using
  207.  #  'doTheCompare'.  We parse its contents.
  208.  # -------------------------------------------------------------------------
  209.  ##
  210. proc Diff::opening {name} {
  211.     global Diff::window DiffmodeVars Diff::leftDir Diff::rightDir
  212.     set Diff::window $name
  213.     set Diff::leftDir ""
  214.     set Diff::rightDir ""
  215.     
  216.     if $DiffmodeVars(useSophisticatedDiffMarking) {
  217.         Diff::parseDiffWin
  218.     }
  219. }
  220.  
  221. # ◊◊◊◊ Parsing diff information ◊◊◊◊ #
  222.  
  223. proc Diff::parseDiffWin {} {
  224.     Diff::diffWinFront
  225.     global diffDir Diff::window
  226.  
  227.     set limit 0
  228.     set pos 0
  229.     while 1 {
  230.         set res [search -s -n -f 1 -r 1 {^(diff.*|[^- \r]+)\r} $pos]
  231.         if {$res != ""} {
  232.             set pos [expr [lindex $res 0] +1]
  233.             # if we picked up a 'diff...' line in a context diff
  234.             if {[lookAt $pos] == "i" && [lookAt [nextLineStart [lindex $res 0]]] == "*"} {
  235.                 continue
  236.             }
  237.             set t [getText [lindex $res 0] [expr [lindex $res 1] -1]]
  238.             if {[regexp {^\*+$} $t]} {
  239.                 set diffDir 1
  240.                 # check if the file has changed
  241.                 if {[string index [set tt [getText [prevLineStart $pos] $pos]] 0] != " " \
  242.                       && [lookAt [expr $pos -3]] != "-" } {
  243.                     set to [lindex $tt 1]
  244.                     regexp { (.*)\t} $tt "" to
  245.                     set p [prevLineStart $pos]
  246.                     regexp { (.*)\t} [getText [prevLineStart $p] $p] from
  247.                     regsub -all "/" $from ":" from
  248.                     regsub -all "/" $to ":" to
  249.                     lappend got [list "diff" $from $to]
  250.                 }
  251.                 set from [lindex [eval getText [search -s -n -f 1 -r 1 {^\*\*\* [0-9]+,[0-9]+} $pos]] 1]
  252.                 set to [lindex [eval getText [search -s -n -f 1 -r 1 {^--- [0-9]+,[0-9]+} $pos]] 1]
  253.                 lappend got "$from $to"
  254.             } else {
  255.                 lappend got $t
  256.             }
  257.         } else {
  258.             break
  259.         }
  260.     }
  261.     set Diff::window [win::Current]
  262.     # now stored all diff items in the list 'got'
  263.     if [info exists got] {Diff::storeMarks $got}
  264.     Diff::diffWinFront
  265.     global tileTop tileWidth tileHeight tileLeft
  266.     set top [expr $tileTop + $tileHeight - 178]
  267.     sizeWin ${Diff::window} [expr $tileWidth - 6] 178
  268.     moveWin ${Diff::window} $tileLeft $top
  269.  
  270. }
  271.  
  272. proc Diff::storeMarks {diffs} {
  273.     global Diff::1 Diff::2 Diff::array
  274.     set suff ""
  275.     foreach m $diffs {
  276.         if [regexp {^diff} $m] {
  277.             set suff "/[file tail [lindex $m end]]"
  278.             global Diff::array${suff}
  279.             continue
  280.         }
  281.         set Diff::array${suff}($m) ""
  282.     }
  283. }
  284.  
  285. proc Diff::setMarksUp {{suff ""}} {
  286.     global Diff::array${suff}
  287.     foreach m [array names Diff::array$suff] {
  288.         set scanned [Diff::parseDiffString $m]
  289.         if {[scan $scanned "%s %d %d %d %d" \
  290.             char start1 end1 start2 end2] != 5} { error "Bad diff list!" }
  291.         if {$scanned != ""} {
  292.             set Diff::array${suff}($m) $scanned
  293.         }
  294.     }
  295. }
  296.  
  297. proc Diff::mark {win left {suff ""}} {
  298.     global Diff::array$suff DiffmodeVars
  299.     if {$win != ""} {
  300.         # Alpha somehow remembers the last mode in which it adjusts
  301.         # the window and so forgets all the colours if we cheat the
  302.         # mode switch.
  303.         if $DiffmodeVars(workaroundAlphaColourBug) {
  304.             bringToFront $win
  305.         } else {
  306.             Diff::bringToFront $win
  307.         }
  308.         # not strictly necessary, but cleaner
  309.         removeAllMarks diff-*
  310.         if $left {
  311.             foreach m [array names Diff::array$suff] {
  312.                 scan [set Diff::array${suff}($m)] "%s %d %d" char start1 end1
  313.                 setNamedMark "diff-$m" $start1 $start1 $end1
  314.             }
  315.         } else {
  316.             foreach m [array names Diff::array$suff] {
  317.                 scan [set Diff::array${suff}($m)] "%s %d %d %d %d" char start1 end1 start2 end2
  318.                 setNamedMark "diff-$m" $start2 $start2 $end2
  319.             }
  320.         }
  321.     }
  322. }
  323.  
  324. proc Diff::markUpWindow {diffs} {
  325.     alertnote "Currently a little obsolete; shouldn't be called!"
  326.     if [info exists Diff::1] {
  327.         Diff::bringToFront ${Diff::1}
  328.         # not strictly necessry, but cleaner
  329.         removeAllMarks diff-*
  330.         foreach m $diffs {
  331.             scan [set Diff::array($m)] "%s %d %d" char start1 end1
  332.             setNamedMark "diff-$m" $start1 $start1 $end1
  333.         }
  334.     }
  335.     if [info exists Diff::2] {
  336.         Diff::bringToFront ${Diff::2}
  337.         # not strictly necessry, but cleaner
  338.         removeAllMarks diff-*
  339.         foreach m $diffs {
  340.             scan [set Diff::array($m)] "%s %d %d %d %d" char start1 end1 start2 end2
  341.             setNamedMark "diff-$m" $start2 $start2 $end2
  342.         }
  343.     }
  344.     
  345. }
  346.  
  347. proc Diff::parseDiffString {text} {
  348.     global Diff::1 Diff::2
  349.     if {![regexp {[acd]} $text char]} {
  350.         # context sensitive
  351.         set char "c"
  352.         if {[scan $text "%d,%d %d,%d" one oned two twod] != 4} {
  353.             return
  354.         }
  355.     } else {
  356.         set res [split $text $char]
  357.         if {![scan [lindex $res 0] "%d,%d" one oned]} return
  358.         if {![scan [lindex $res 1] "%d,%d" two twod]} return
  359.         if ![info exists oned] { set oned $one }
  360.         if ![info exists twod] { set twod $two }
  361.     }
  362.     
  363.     if [info exists Diff::1] {
  364.         set res [list $char [rowColToPos -w ${Diff::1} $one 0]]
  365.         if {$char != "a"} {
  366.             lappend res [rowColToPos -w ${Diff::1} [expr $oned + 1] 0]
  367.         } else {
  368.             lappend res [rowColToPos -w ${Diff::1} $oned 1]
  369.         }
  370.     } else {
  371.         set res [list $char -1 -1]
  372.     }
  373.     
  374.     if [info exists Diff::2] {
  375.         lappend res [rowColToPos -w ${Diff::2} $two 0]
  376.         if {$char != "d"} {
  377.             lappend res [rowColToPos -w ${Diff::2} [expr $twod + 1] 0]]
  378.         } else {
  379.             lappend res [rowColToPos -w ${Diff::2} $twod 1]
  380.         }
  381.     } else {
  382.         lappend res -1 -1
  383.     }
  384.     return $res
  385. }
  386.  
  387. proc Diff::parseDiffLine {text {is_pos 0}} {
  388.     if {$is_pos} {
  389.         set text [Diff::line $text]
  390.     }
  391.     return [Diff::parseDiffString $text]
  392. }
  393.  
  394. proc Diff::line {pos {f ""}} {
  395.     global diffDir Diff::window DiffmodeVars
  396.     if $diffDir {
  397.         if {$f != ""} {upvar $f files}
  398.         if {[lookAt $pos] == "*" || [catch {search -s -f 0 -r 1 {^diff.*\r} $pos} res]} {
  399.             set p $pos
  400.             while 1 {
  401.                 set res [search -s -f 0 -r 1 {^\*+\r} $p]
  402.                 set p [expr [lindex $res 0] -2]
  403.                 if {[lookAt [lineStart $p]] != " " && [lookAt $p] != "-"} break
  404.             }
  405.             regexp { (.*)\t} [getText [lineStart $p] $p] "" to
  406.             regexp { (.*)\t} [getText [prevLineStart $p] [lineStart $p]] "" from
  407.             if {[set pr $DiffmodeVars(removeFilePrefix)] != ""} {
  408.                 regsub -all "/\./" $to "/" to
  409.                 if {[string first $pr $to] == 0} {
  410.                     set to [string range $to [string length $pr] end]
  411.                 }
  412.                 regsub -all "/\./" $from "/" from
  413.                 if {[string first $pr $from] == 0} {
  414.                     set from [string range $from [string length $pr] end]
  415.                 }
  416.             }
  417.             set files [list $from $to]
  418.             set tfrom [lindex [eval getText [search -s -n -f 1 -r 1 {^\*\*\* [0-9]+,[0-9]+} [getPos]]] 1]
  419.             set tto [lindex [eval getText [search -s -n -f 1 -r 1 {^--- [0-9]+,[0-9]+} [getPos]]] 1]
  420.             set text "$tfrom $tto"        
  421.         } else {
  422.             set llen [llength [set files [eval getText $res]]]
  423.             set files [lrange $files [expr $llen -2] end]
  424.             set text [getText [lineStart $pos] [expr [nextLineStart $pos] - 1]]
  425.         }
  426.         if $DiffmodeVars(convertSlashToColonInPaths) {
  427.             regsub -all "/" $files ":" files
  428.         }
  429.         set f [lindex $files end]
  430.         set suff "/[file tail $f]"
  431.     } else {
  432.         set suff ""
  433.         set text [getText [lineStart $pos] [expr [nextLineStart $pos] - 1]]
  434.     }
  435.     return "${text}${suff}"
  436. }
  437.  
  438. # ◊◊◊◊ Patching routines ◊◊◊◊ #
  439. proc Diff::patch {w1 w2 left} {
  440.     global DiffmodeVars
  441.     if {$DiffmodeVars(useSophisticatedDiffMarking)} {
  442.         Diff::patchSophisticated $w1 $w2 $left
  443.     } else {
  444.         Diff::patchOld $w1 $w2 $left
  445.     }
  446. }
  447. proc Diff::patchSophisticated {ww1 ww2 left} {
  448.     upvar \#0 $ww1 w1
  449.     upvar \#0 $ww2 w2
  450.     set code [Diff::line [getPos]]
  451.     global Diff::array
  452.     regexp {([^/]+)(.*)} $code "" mark suff
  453.     if ![info exists w1] { alpha::errorAlert "No such window" }
  454.     switch "[lindex [set Diff::array${suff}($mark)] 0]${left}" {
  455.         "c1" -
  456.         "c0" {
  457.             if [info exists w2] { 
  458.                 Diff::bringToFront ${w2}
  459.                 gotoMark "diff-$mark"
  460.                 set text [getSelect]
  461.             } else {
  462.                 # we assume the line is selected in the diff-win
  463.                 if $left {
  464.                     set p [selEnd]
  465.                     set e [search -s -f 1 -r 1 {^---.*$} $p]
  466.                     set p [lindex $e 1]
  467.                     set e [search -s -f 1 -r 1 {^[^>]} $p]
  468.                     set text [getText $p [lindex $e 0]]
  469.                     regsub -all "\r> " $text "\r" text
  470.                     set text [string range $text 1 end]
  471.                 } else {
  472.                     set p [selEnd]
  473.                     set e [search -s -f 1 -r 1 {^---} $p]
  474.                     set text [getText $p [lindex $e 0]]
  475.                     regsub -all "\r< " $text "\r" text
  476.                     set text [string range $text 1 end]
  477.                 }
  478.             }
  479.             Diff::bringToFront ${w1}
  480.             gotoMark "diff-$mark"
  481.             replaceText [getPos] [selEnd] $text
  482.         }
  483.         "d1" -
  484.         "a0" {
  485.             Diff::bringToFront ${w1}
  486.             gotoMark "diff-$mark"
  487.             deleteText [getPos] [selEnd]
  488.         }
  489.         "a1" -
  490.         "d0" {
  491.             if [info exists w2] { 
  492.                 Diff::bringToFront ${w2}
  493.                 gotoMark "diff-$mark"
  494.                 set text [getSelect]
  495.             } else {
  496.                 # we assume the line is selected in the diff-win
  497.                 if $left {
  498.                     set p [selEnd]
  499.                     set e [search -s -f 1 -r 1 {^---.*$} $p]
  500.                     set p [lindex $e 1]
  501.                     set e [search -s -f 1 -r 1 {^[^>]} $p]
  502.                     set text [getText $p [lindex $e 0]]
  503.                     regsub -all "\r> " $text "\r" text
  504.                     set text [string range $text 1 end]
  505.                 } else {
  506.                     set p [selEnd]
  507.                     set e [search -s -f 1 -r 1 {^---} $p]
  508.                     set text [getText $p [lindex $e 0]]
  509.                     regsub -all "\r< " $text "\r" text
  510.                     set text [string range $text 1 end]
  511.                 }
  512.             }
  513.             Diff::bringToFront ${w1}
  514.             gotoMark "diff-$mark"
  515.             nextLine
  516.             insertText -w ${w1} $text
  517.         }
  518.     }
  519.     Diff::diffWinFront
  520. }
  521. proc Diff::patchOld {ww1 ww2 left} {
  522.     upvar \#0 $ww1 w1
  523.     upvar \#0 $ww2 w2
  524.     set code [Diff::line [getPos]]
  525.     if {[scan [Diff::parseDiffLine $code] "%s %d %d %d %d" \
  526.         char start1 end1 start2 end2] != 5} { return }
  527.     
  528.     switch $char${left} {
  529.         "c1" {
  530.             set text [getText -w ${w2} $start2 $end2]
  531.             bringToFront ${w1}
  532.             replaceText $start1 $end1 $text
  533.         }
  534.         "d1" {
  535.             bringToFront ${w1}
  536.             deleteText $start1 $end1
  537.         }
  538.         "a1" {
  539.             set text [getText -w ${w2} $start2 $end2]
  540.             set p [nextLineStart $start1]
  541.             # for some reason this single line won't work instead of the
  542.             # next two!
  543.             #select -w ${Diff::1} $p $p
  544.             bringToFront ${w1}
  545.             goto $p
  546.             insertText -w ${w1} $text
  547.         }
  548.         "c0" {
  549.             set text [getText -w ${w2} $start1 $end1]
  550.             bringToFront ${w1}
  551.             replaceText $start2 $end2 $text
  552.         }
  553.         "d0" {
  554.             set text [getText -w ${w2} $start1 $end1]
  555.             bringToFront ${w1}
  556.             goto $start2
  557.             nextLine
  558.             insertText $text
  559.         }
  560.         "a0" {
  561.             bringToFront ${w1}
  562.             deleteText $start2 $end2
  563.         }
  564.     }
  565.     message "Subsequent insertions will be screwed up"
  566. }
  567.  
  568. # In the diff-window, 'c' = cut from left, replace with given lines,
  569. # 'd' = delete from left, 'a' = add to left.
  570. proc Diff::patchIntoLeftWindow {} {
  571.     Diff::patch Diff::1 Diff::2 1
  572. }
  573.  
  574. proc Diff::patchIntoRightWindow {} {
  575.     Diff::patch Diff::2 Diff::1 0
  576. }
  577.  
  578. # ◊◊◊◊ Main comparison routines ◊◊◊◊ #
  579.  
  580. proc Diff::files {} {
  581.     global Diff::1 Diff::2
  582.     foreach f [list ${Diff::1} ${Diff::2}] {
  583.         if {[lsearch [winNames -f] $f] >= 0} {
  584.             getWinInfo -w $f arr
  585.             if $arr(dirty) {
  586.                 bringToFront $f
  587.                 if {![dialog::yesno "Save this window?"]} { error "Cancel"}
  588.                 save
  589.             }
  590.         }
  591.     }
  592.     # make sure newer file is on the right
  593.     if [file::secondIsOlder ${Diff::1} ${Diff::2}] {
  594.         set d ${Diff::2}
  595.         set Diff::2 ${Diff::1}
  596.         set Diff::1 $d
  597.         unset d
  598.     }
  599.     
  600.     Diff::Display Diff::1 1 0 1
  601.     Diff::Display Diff::2 0 0 1
  602.  
  603.     doTheCompare
  604. }
  605.  
  606. proc compare::directories {} {
  607.     global Diff::1 Diff::2
  608.     
  609.     set Diff::1 [string trimright [get_directory -p "Select 'old' dir 1:"] {:}]
  610.     set Diff::2 [string trimright [get_directory -p "Select 'new' dir 2:"] {:}]
  611.  
  612.     doTheCompare 1 {* Directory Comparison *}
  613. }
  614.  
  615. proc compare::files {} {
  616.     global Diff::1 Diff::2
  617.  
  618.     set Diff::1 [getfile "Select your 'old' file:"]
  619.     set Diff::2 [getfile "Select your 'new' file:"]
  620.     
  621.     Diff::files
  622. }
  623.  
  624. proc compare::windows {} {
  625.     global tileHeight tileWidth tileTop tileLeft
  626.     global Diff::1 Diff::2
  627.  
  628.     set wins [winNames -f]
  629.     if {[llength $wins] < 2} { message "Need 2 windows"; return }
  630.     
  631.     set Diff::1 [lindex $wins 0]
  632.     set Diff::2 [lindex $wins 1]
  633.     Diff::files
  634. }
  635.  
  636.  
  637. ## 
  638.  # -------------------------------------------------------------------------
  639.  # 
  640.  # "doTheCompare" --
  641.  # 
  642.  #  Modification of the original to optionally return the diff 
  643.  #  result, rather than opening it in a window
  644.  # 
  645.  # Results:
  646.  # 
  647.  #  Returns 1 if the files are the same and 0 if they differ
  648.  #  
  649.  #  If the global flag returnDiff is non-zero, 
  650.  #  the result of the diff operation is stored in diffResult, rather
  651.  #  than being opened in a window
  652.  # 
  653.  # --Version--Author------------------Changes-------------------------------
  654.  #    1.0     <keleher@cs.umd.edu> original
  655.  #    1.1     <j-guyer@nwu.edu> optionally return diff result in a global
  656.  #    1.2     <j-guyer@nwu.edu> flags set if files were open before compare
  657.   # -------------------------------------------------------------------------
  658.  ##
  659. proc doTheCompare {{isdir 0} {name {* File Comparison *}} {showAlert 1}} {
  660.     global tileLeft tileTop tileWidth tileHeight DiffmodeVars
  661.     global Diff::1 Diff::2 win::Modes HOME diffDir returnDiff diffResult
  662.     global Diff::1Open Diff::2Open
  663.     global Diff::leftDir Diff::rightDir
  664.  
  665.     set Diff::leftDir ""
  666.     set Diff::rightDir ""
  667.     set diffDir $isdir
  668.  
  669.     message "Launching 'GNU Diff'"
  670.     launch "$HOME:Tools:GNU Diff"
  671.     message "Starting diff…"
  672.     set flags $DiffmodeVars(diffFlags)
  673.     if {$DiffmodeVars(linesOfContext) != 0} {
  674.         append flags " -C $DiffmodeVars(linesOfContext)"
  675.     }
  676.     set dtext [dosc -n "GNU Diff" -s "$flags \"[stripNameCount ${Diff::1}]\" \"[stripNameCount ${Diff::2}]\""]
  677.     message "Starting diff…done"
  678.     
  679.     if {[lsearch [winNames -f] ${Diff::1}] >= 0} {
  680.         set Diff::1Open 1
  681.     } else {
  682.         set Diff::1Open 0
  683.     }
  684.     if {[lsearch [winNames -f] ${Diff::2}] >= 0} {
  685.         set Diff::2Open 1
  686.     } else {
  687.         set Diff::2Open 0
  688.     }
  689.  
  690.     if {![string length $dtext]} {
  691.         if {$showAlert} {
  692.             alertnote    "No difference:\r${Diff::1}\r${Diff::2}"
  693.         }
  694.         return 0
  695.     } else {    
  696.         # If requested, return the diff result in diffReturn, 
  697.         # rather than opening a diff window
  698.         if {[info exists returnDiff] && [expr $returnDiff != 0]} {
  699.             set diffResult $dtext
  700.         } else {
  701.             set top [expr $tileTop + $tileHeight - 178]
  702.             set n [new -n $name -g $tileLeft $top [expr $tileWidth - 6] 178 -m Diff]
  703.             insertText "\r$dtext\r"
  704.             winReadOnly
  705.             select 0 [nextLineStart 0]
  706.             Diff::opening $n
  707.         }
  708.         return 1
  709.     }
  710. }
  711.  
  712. # ◊◊◊◊ Moving around ◊◊◊◊ #
  713. proc Diff::Up {} {
  714.     set limit 0
  715.     set res [search -s -f 0 -r 1 {^[^- \r]+\r} [expr [getPos] - 1]]
  716.     set pos [lindex $res 0]
  717.     select $pos [nextLineStart $pos]
  718.     display $pos
  719.     refresh
  720. }
  721.  
  722. proc Diff::Down {} {
  723.     set limit 0
  724.     set res [search -s -f 1 -r 1 {^[^- \r]+\r} [expr [getPos] + 1]]
  725.     set pos [lindex $res 0]
  726.     select $pos [nextLineStart $pos]    
  727.     display $pos
  728.     refresh
  729. }
  730.  
  731. proc Diff::Select {} {
  732.     global Diff::1 Diff::2 diffDir
  733.     
  734.     set text [getText [lineStart [getPos]] [expr [nextLineStart [getPos]] - 1]]
  735.     
  736.     if {![regexp {[acd]} $text char]} return
  737.     set res [split $text $char]
  738.     if {![scan [lindex $res 0] "%d" one]} return
  739.     if {![scan [lindex $res 1] "%d" two]} return
  740.     if {$one == 1} {incr one}
  741.     if {$two == 1} {incr two}
  742.  
  743.     if {$diffDir} {
  744.         set res [search -s -f 0 -r 1 "^diff.*\r" [getPos]]
  745.         set text [eval getText $res]
  746.         set len [llength $text]
  747.         set Diff::1 [lindex $text [expr $len - 2]]
  748.         set Diff::2 [lindex $text [expr $len - 1]]
  749.     }
  750.     Diff::Display Diff::1 1 [expr $one - 1] $diffDir
  751.     Diff::Display Diff::2 0 [expr $two - 1] $diffDir
  752.  
  753.     if {$diffDir} {
  754.         catch {bringToFront ${Diff::window}}
  755.     }
  756. }
  757.  
  758. proc Diff::Display {name left {row 0} {check 0}} {
  759.     upvar $name wname
  760.     if ![info exists wname] {
  761.         if $left {
  762.             message "Diff window for left doesn't exist"
  763.         } else {
  764.             message "Diff window for right doesn't exist"
  765.         }
  766.         return
  767.     }
  768.     if {$check} {
  769.         set geo [Diff::Geo $left]
  770.         if {[set res [lsearch [winNames -f] "$wname*"]] < 0} {
  771.             eval edit -g $geo [list $wname]
  772.             set wname [win::Current]
  773.         } else {
  774.             set wname [lindex [winNames -f] $res]
  775.             if {[getGeometry $wname] != $geo} {
  776.                 sizeWin $wname [lindex $geo 2] [lindex $geo 3]
  777.                 moveWin $wname [lindex $geo 0] [lindex $geo 1]
  778.             }
  779.             if {$res > 2} {
  780.                 bringToFront $wname
  781.             }
  782.         }
  783.     }
  784.     display -w $wname [rowColToPos -w $wname $row 0]
  785. }
  786.  
  787. proc Diff::viewSophisticated {} {
  788.     global Diff::1 Diff::2 diffDir DiffmodeVars Diff::Marked
  789.     global Diff::leftDir Diff::rightDir
  790.     
  791.     set text [Diff::line [getPos] files]
  792.  
  793.     if {$diffDir} {
  794.         set Diff::1 ${Diff::leftDir}[lindex $files 0]
  795.         if ![file exists ${Diff::1}] {
  796.             if {${Diff::leftDir} == "" && ([set res [lsearch [winNames] "[file tail ${Diff::1}]*"]] != -1)} {
  797.                 set Diff::1 [lindex [winNames -f] $res]
  798.             } else {
  799.                 unset Diff::1
  800.             }
  801.         } else {
  802.             if {[set res [lsearch [winNames -f] "${Diff::1}*"]] != -1} {
  803.                 set Diff::1 [lindex [winNames -f] $res]
  804.             }
  805.         }
  806.         set Diff::2 ${Diff::rightDir}[lindex $files 1]
  807.         if ![file exists ${Diff::2}] {
  808.             if {${Diff::rightDir} == "" && ([set res [lsearch [winNames] "[file tail ${Diff::2}]*"]] != -1)} {
  809.                 set Diff::2 [lindex [winNames -f] $res]
  810.             } else {
  811.                 unset Diff::2
  812.             }
  813.         } else {
  814.             if {[set res [lsearch [winNames -f] "${Diff::2}*"]] != -1} {
  815.                 set Diff::2 [lindex [winNames -f] $res]
  816.             }
  817.         }
  818.     }
  819.     regexp {([^/]+)(.*)} $text "" mark suff
  820.     if ![info exists "Diff::Marked($suff)"] {
  821.         Diff::Display Diff::1 1 0 1
  822.         Diff::Display Diff::2 0 0 1
  823.         Diff::setMarksUp $suff
  824.         if [info exists Diff::1] {
  825.             Diff::mark ${Diff::1} 1 $suff
  826.             set Diff::Marked($suff) 1
  827.         }
  828.         if [info exists Diff::2] {
  829.             Diff::mark ${Diff::2} 0 $suff
  830.             set Diff::Marked($suff) 1
  831.         }
  832.         Diff::diffWinFront
  833.     }
  834.     set text $mark
  835.  
  836.     if $DiffmodeVars(useMarksDontBringToFront) {
  837.         if ![catch {getNamedMarks -w ${Diff::1}} marks] {
  838.             regexp {(.*) <[0-9]+>$} [set f ${Diff::1}] dmy f
  839.             regexp "\{ \{diff-$text\} \{[quote::Regfind $f]\} (\[0-9\]+) \[0-9\]+ (\[0-9\]+) \}" $marks dummy beg end
  840.             display -w ${Diff::1} [expr $beg > 0 ? $beg -1 : $beg]
  841.             select -w ${Diff::1} $beg $end
  842.             if {$beg > 0} {incr beg -1}
  843.             #display -w ${Diff::1} $beg
  844.             #refresh ${Diff::1}
  845.         }
  846.         
  847.         if ![catch {getNamedMarks -w ${Diff::2}} marks] {
  848.             regexp {(.*) <[0-9]+>$} [set f ${Diff::2}] dmy f
  849.             regexp "\{ \{diff-$text\} \{[quote::Regfind $f]\} (\[0-9\]+) \[0-9\]+ (\[0-9\]+) \}" [getNamedMarks -w ${Diff::2}] dummy beg end
  850.             display -w ${Diff::2} [expr $beg > 0 ? $beg -1 : $beg]
  851.             select -w ${Diff::2} $beg $end
  852.             if {$beg > 0} {incr beg -1}
  853.             #display -w ${Diff::2} $beg
  854.             #refresh ${Diff::2}
  855.         }
  856.         # we need this line because of an Alpha visual bug.
  857.         # Alpha will often draw the text in the wrong window when we 
  858.         # hit 'down'.  It does correct itself, but it looks silly.
  859.         Diff::diffWinFront
  860.     } else {            
  861.         if ![catch {Diff::bringToFront ${Diff::1}}] {
  862.             gotoMark "diff-$text"
  863.         }
  864.         if ![catch {Diff::bringToFront ${Diff::2}}] {
  865.             gotoMark "diff-$text"        
  866.         }
  867.         Diff::diffWinFront
  868.     }
  869. }
  870. proc Diff::viewOld {} {
  871.     global Diff::1 Diff::2 diffDir 
  872.     
  873.     set text [Diff::line [getPos]]
  874.     if {![regexp {[acd]} $text char]} return
  875.     set res [split $text $char]
  876.     if {![scan [lindex $res 0] "%d,%d" one oned]} return
  877.     if {![scan [lindex $res 1] "%d,%d" two twod]} return
  878.     set on $one
  879.     set tw $two
  880.     if {$on == 1} {incr on}
  881.     if {$tw == 1} {incr tw}
  882.     if {![info exists oned]} {set oned $one}
  883.     if {![info exists twod]} {set twod $two}
  884.  
  885.     if {$diffDir} {
  886.         set res [search -s -f 0 -r 1 "^diff.*\r" [getPos]]
  887.         set text [eval getText $res]
  888.         set Diff::1 [lindex $text 1]
  889.         set Diff::2 [lindex $text 2]
  890.     }
  891.     Diff::Sel Diff::1 [expr $on - 1] $one $oned 1
  892.     Diff::Sel Diff::2 [expr $tw - 1] $two $twod 0
  893.     set wins [lremove [lrange [winNames -f] 0 2] ${Diff::1} ${Diff::2}]
  894.     set wins [lremove -glob $wins *Comparison*]
  895.     if {$wins != ""} {
  896.         bringToFront ${Diff::1}
  897.         bringToFront ${Diff::2}
  898.     }
  899.     Diff::diffWinFront
  900. }
  901.  
  902. proc Diff::View {} {
  903.     global DiffmodeVars
  904.     if {$DiffmodeVars(useSophisticatedDiffMarking)} {
  905.         Diff::viewSophisticated
  906.     } else {
  907.         Diff::viewOld
  908.     }
  909. }
  910.  
  911. ## 
  912.  # -------------------------------------------------------------------------
  913.  # 
  914.  # "Diff::Sel" --
  915.  # 
  916.  #  This handles a name either with or without trailing '<n>' and fixes
  917.  #  the given name if it isn't right.
  918.  # -------------------------------------------------------------------------
  919.  ##
  920. proc Diff::Sel {wnamev ro row rowd left} {
  921.     global diffDir
  922.     upvar $wnamev wname
  923.     if {$diffDir} {
  924.         set geo [Diff::Geo $left]
  925.         if {[set res [lsearch [winNames -f] "$wname*"]] < 0} {
  926.             eval edit -g $geo [list $wname]
  927.             set wname [win::Current]
  928.         } else {
  929.             set wname [lindex [winNames -f] $res]
  930.             if {[getGeometry $wname] != $geo} {
  931.                 sizeWin $wname [lindex $geo 2] [lindex $geo 3]
  932.                 moveWin $wname [lindex $geo 0] [lindex $geo 1]
  933.             }
  934.         }
  935.     }
  936.     display -w $wname [rowColToPos -w $wname $ro 0]
  937.     select -w $wname [rowColToPos -w $wname $row 0] [rowColToPos -w $wname [expr $rowd + 1] 0]
  938. }
  939.  
  940. # ◊◊◊◊ Utilities ◊◊◊◊ #
  941.  
  942. proc Diff::Win {} {
  943.     global win::Modes
  944.     set files [winNames -f]
  945.     set len [llength $files]
  946.         for {set i 0} {$i < $len} {incr i} {
  947.             if {[set win::Modes([lindex [winNames -f] $i])] == "Diff"} {
  948.                 bringToFront [lindex [winNames] $i]
  949.                 return
  950.             }
  951.         }
  952.     beep
  953.     message "No Diff window."
  954. }
  955.  
  956. proc Diff::Geo {left} {
  957.     global tileWidth tileHeight tileTop tileLeft
  958.  
  959.     set margin 4
  960.     set width [expr ($tileWidth - $margin)/2]
  961.     set height [expr $tileHeight - 200]
  962.     set hor $tileLeft
  963.  
  964.     if {!$left} {incr hor [expr $width+$margin]}
  965.     
  966.     return [list $hor $tileTop $width $height]
  967. }
  968.  
  969. proc Diff::diffWinFront {} {
  970.     global Diff::window
  971.     catch {bringToFront ${Diff::window}}
  972. }
  973.  
  974. ## 
  975.  # -------------------------------------------------------------------------
  976.  # 
  977.  # "Diff::bringToFront" --
  978.  # 
  979.  #  Hack to make it quicker to switch between windows.  We often want
  980.  #  the 'Diff' window to be in the front all the time, but have to
  981.  #  bring others to the front temporarily for manipulation.  This proc
  982.  #  brings a different window to the front more quickly by avoiding
  983.  #  all mode-changing code.  Of course you should only call this proc
  984.  #  when you will _very_ soon bring a different window to the front.
  985.  # -------------------------------------------------------------------------
  986.  ##
  987. proc Diff::bringToFront {w} {
  988.     global win::Modes DiffmodeVars
  989.     if $DiffmodeVars(useFastWindowSwapping) {
  990.         set oldm [set win::Modes($w)]
  991.         set win::Modes($w) Diff
  992.         if [catch {bringToFront $w}] {
  993.             unset win::Modes($w)
  994.             beep
  995.             error "no such win"
  996.         } else {
  997.             set win::Modes($w) $oldm
  998.         }
  999.     } else {
  1000.         bringToFront $w
  1001.     }
  1002. }
  1003.  
  1004.  
  1005.